//#include<regx51.h>
#include <REGX52.H>
#include <IR.H>
#include <intrins.h>
#include <stdlib.h>
#define M 12		//难度系数，12中等，越小越难	
typedef unsigned char uchar;
typedef unsigned int uint;
unsigned char Command;
sbit HC595_SRCLK = P3^6;		  //595芯片
sbit HC595_RCLK = P3^5;
sbit SER = P3^4;

sbit LSA=P2^2;			 //138译码器
sbit LSB=P2^3;
sbit LSC=P2^4;

uchar code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
							0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值

					//定义方向键
sbit K1 = P3^1;		//上 
sbit K2 = P3^0;		//下 
sbit K3 = P3^2;		//左
sbit K4 = P3^3;		//右 

uchar m;			//储存果子的X坐标
uchar n;			//储存果子的Y坐标

uchar snakelong = 3;	   //初始化蛇长
char flag_x = 1;		   //初始化运动方向
char flag_y = 0;		   //初始化运动方向
uchar flag_gg = 0;		   //生成果子的重复性标志
uchar flag_c = 0, sheshen = 0;	  //吃果标志
uchar flag_a = 0;		//移动标志
uchar flag_s = 0;		//死亡标志
uchar x_s[32] = {0},y_s[32] = {0};	   //定义蛇的最大长度
uchar code X_[8] = {0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0xFE};	 
uchar code Y_[8] = {0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};	   

/*  
	y1				//定义x,y坐标
	y2			  //如，我想让（4，2）亮，只需要P0 = X_[4];HC_595 = Y_[2];
	y3
	y3
	y5
	y6
	y7
	y8
	  x1    x2   x3   x4  x5   x6   7   x8
*/


void suijishu();				   //提前定义随机数生成函数
void delay(uint i)				   //简单的delay函数，对于51单片机，delay（1）约等于9微秒
{
	while(i--);
}



void Timer0Init()				  //定时器初始化函数	   模式1，16位定时器
{
//	TMOD|=0X01;//选择为定时器0模式，工作方式1，仅用TR0打开启动。
//	TH1=0XFC;	//给定时器赋初值，定时1ms
//	TL1=0X18;	
//	ET1=1;//打开定时器0中断允许
//	EA=1;//打开总中断
//	TR1=1;//打开定时器		

	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x10;		//设置定时器模式
	TL1 = 0x9C;		//设置定时初值
	TH1 = 0xFF;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 1;		//定时器1开始计时
	ET1=1;
	EA=1;
	PT1=0;
}
void HC_595(uchar date)				 //595例程，这里就不详细解释了
{	
	uchar a;
	HC595_SRCLK = 0;
	HC595_RCLK = 0;

	for(a=0;a<8;a++)
	{
		SER = date>>7;
		date<<=1;
		HC595_SRCLK = 1;
		_nop_();
		_nop_();
		HC595_SRCLK = 0;
	}
	HC595_RCLK = 1;
		_nop_();
		_nop_();
	HC595_RCLK = 0;
}


void display()						//数码管显示，用来显示蛇长
{
	uchar i,b=2;
	HC_595(0x00);     //消隐
	_nop_();
	P0=0x00;		  //因为我用的板子上数码管和点阵共用的P0口，所以这里消隐的时候需要特别注意。
	_nop_();
		for(i=0;i<2;i++)
	{	
		while(b) {		   //这里循环两次是为了让数码管显示更清楚一点。
			switch(i)	 //位选，选择点亮的数码管
			{
				case(0):
					LSA=0;LSB=0;LSC=0; P0=smgduan[(snakelong%10)];delay(10);P0=0x00;break;	
				case(1):
					LSA=1;LSB=0;LSC=0; P0=smgduan[(snakelong/10)];delay(10);P0=0x00;break;
			}	
				_nop_();
				b--;
				}		
	}
	LSA=1;LSB=1;LSC=1; P0=0xFF;	 //还是为了消隐。以一个数码管常亮为代价，以求不影响点阵和数码管显示蛇长      
}

void appear()			// viod 蛇身出现   		 
{	
/*	x_s[0] = 2;			 //初始化三个点
	y_s[0] = 3;
	x_s[1] = 3;
	y_s[1] = 3;
	x_s[2] = 4;
	y_s[2] = 3;	 */
							     
//uint t = 150;	 //t越小越难，越大越简单		 //这里也可以控制难度，经测试也有效。
//while(t)
//{	
	uchar i;
	for(i=0;i<snakelong;i++)					 //snakelong 是用来控制数组的全局关键变量
	{
		if((x_s[i]!=0)&&(y_s[i]!=0))
		{
			P0 = X_[x_s[i]-1];
			HC_595(Y_[y_s[i]-1]);
			delay(100);		   //延时
			P0 = 0xFF;		 //消隐	
			HC_595(0x00);     //消隐
		}
	}
//	t--;
//}

}


void auto_move()		// viod 自动移动 		  核心程序之一
{	
	uchar i;					
	for(i=0;i<(snakelong-1);i++)
	{
		x_s[i] = x_s[i+1];
		y_s[i] = y_s[i+1];
	}
		x_s[snakelong-1] += flag_x;
		y_s[snakelong-1] += flag_y;

			if(x_s[snakelong-1]>8)			// void 穿墙判断  
				x_s[snakelong-1] = 1;
				else if(x_s[snakelong-1]<1)
					x_s[snakelong-1] = 8;
				
			if(y_s[snakelong-1]>8)
				y_s[snakelong-1] = 1;
				else if(y_s[snakelong-1]<1)
					y_s[snakelong-1] = 8;
	
}


void direction()		// void 方向控制 
{
	if(IR_GetDataFlag())	//如果收到数据帧
		{
			Command=IR_GetCommand();
			if(Command==IR_2) 
			{
				//delay(1000);
				//if(Command==IR_2)
				//{
					if(flag_y == 1)	   //是否正在下降	 ，若是，则点击上升无用。
					{flag_y = 1;}
					 else
						{
						flag_x = 0;
						flag_y = -1;	//shang
					}
				//}
			}
			else if(Command==IR_8) 
			{
//				delay(1000);
//				if(Command==IR_8)
//				{
					if(flag_y == -1)	   //是否正在上升
						{flag_y = -1;}
					else
					{
						flag_x = 0;
						flag_y = 1;		//xia
					}
				//}
			}
			else if(Command==IR_4) 
			{
				//delay(1000);
				//if(Command==IR_4)
				//{
					if(flag_x == 1)	   //是否正在右移
					{flag_x = 1;}
					else
					{
						flag_x = -1;	//zuo
						flag_y = 0;	
					}
				//}
			}
			else if(Command==IR_6) 
			{
				//delay(1000);
				//if(Command==IR_6)
				//{
					if(flag_x == -1)	   //是否正在左移
					{flag_x = -1;}
					else
					{
						flag_x = 1;		//you
						flag_y = 0;	
					}
				//}
			}	
		}
}


void chiguo()			// void 吃果判断
{
	if(((x_s[snakelong-1]+flag_x)==m)&&((y_s[snakelong-1]+flag_y)==n)  )
					{ 	flag_c = 1;		}
		else if(((x_s[snakelong-1]+flag_x)==m)&&((y_s[snakelong-1]+flag_y)==n)  ) //重复两次防止程序bug——穿过果子但不去吃     
					{ 	flag_c = 1;		}
			else if(((x_s[snakelong-2]+flag_x)==m)&&((y_s[snakelong-2]+flag_y)==n)  )	 //放宽吃果判定，以防止出现bug——穿过果子但不去吃               
							{	flag_c = 1;	sheshen = 1;    }
				else if(((x_s[snakelong-3]+flag_x)==m)&&((y_s[snakelong-3]+flag_y)==n)  )
								{	flag_c = 1;	sheshen = 2;    }			
   
	if(flag_c)
	{
		flag_c = 0;
		TR0 = 0;//关闭定时器
		snakelong++;
		if(sheshen == 1)
		{
			x_s[snakelong-1] = m+flag_x;
			y_s[snakelong-1] = n+flag_y;
			sheshen = 0;	
		}
		else if(sheshen == 2)
		{
			x_s[snakelong-1] = m+2*flag_x;
			y_s[snakelong-1] = n+2*flag_y;
			sheshen = 0;	
		}
		else
		{							 //这才是吃果的正常情况
		   	x_s[snakelong-1] = m;
			y_s[snakelong-1] = n;
			sheshen = 0;
		}
        suijishu();		  //再生成一个果子
		P0 = 0xFF;		 //消隐	
		HC_595(0x00);     //消隐
		appear();
		appear();
		P0 = 0xFF;		 //消隐	
		HC_595(0x00);     //消隐
		TR0=1;//打开定时器
	}
}


void shensi()			 // void 身死    碰到自己身死
{
	uchar b,x_1,y_1;
	if(snakelong>4)	
	{
	for(b=0;b<snakelong-4;b++)				   //对蛇头处的四个不用判断，也不能判断
	  {	
		if(((x_s[snakelong-1])==x_s[b])&&((y_s[snakelong-1])==y_s[b])  )  
				 {
					flag_s = 1; 
				 }
	  }
	if(flag_s == 1)
		{	
			P0 = 0x00;HC_595(0xFF);delay(2000);	   //闪烁两次
			P0 = 0xFF;HC_595(0x00);delay(2000);
			P0 = 0x00;HC_595(0xFF);delay(3000);
			P0 = 0xFF;HC_595(0x00);delay(1000);
			x_1 = x_s[snakelong-1] ;
			y_1 = y_s[snakelong-1] ;	  		   //卡在死亡状态的下一刻  
		  	snakelong++;
			x_s[snakelong-1] = x_1 + flag_x;
			y_s[snakelong-1] = y_1 + flag_y;
		}

	}
}



void suijishu()						//随机数，生成果子
{
	uchar b,m_1,n_1;
		
	do{
		flag_gg = 0;
		m_1 = (rand()%8)+1;	   				
		n_1 = (rand()%8)+1;
		for(b=0;b<snakelong;b++)
		  {	
			if((m_1==x_s[b])&&(n_1==y_s[b]))	 //如果果子和蛇身有重复，则flag_gg置1，再生成一次                    
					flag_gg = 1;		
		  }
		}while(flag_gg);		
	m = m_1;
	n = n_1;
}


void guozi() interrupt 3		// void 果子出现
{	
	uint a;
	uchar i=3;
	a++;
	direction();	  //用中断去扫描键盘，1mS/次
	P0 = 0xFF;		 //消隐	
	HC_595(0x00);     //消隐
	while(i)		  //重复以增加亮度
	{
	P0 = X_[m-1];
	HC_595(Y_[n-1]);
	i--;
	}
	delay(100);
	P0 = 0xFF;		 //消隐	，因为我用的板子上数码管和点阵共用的P0口，所以这里消隐的时候需要特别注意
	HC_595(0x00);     //消隐

		_nop_();
		P0=0x00;  //数码管消隐
		_nop_();
		display();
		delay(100);

	HC_595(0x00);     //消隐
	_nop_();				
	LSA=1;LSB=1;LSC=1; P0 = 0xFF;
			
	if (a>M)		  //蛇身移动控制位
	{
		a = 0;
		flag_a = 1;
	}
} 


void main()
{	
	x_s[0] = 2;			   //初始化三个点
	y_s[0] = 3;
	x_s[1] = 3;
	y_s[1] = 3;
	x_s[2] = 4;
	y_s[2] = 3;
	Timer0Init();
	IR_Init();
	suijishu();
	//HC595_SRCLK=0;
	//HC595_RCLK=0;
	while(1)
	{
	   appear();
		   if(flag_a)		//蛇身移动位
		   {
				   if(!flag_s)	  //蛇身死亡位
				    {
						auto_move();
						HC_595(0x00);     //消隐
						P0 = 0xFF;		 //消隐	
						shensi();
						chiguo();
						delay(1000);
						flag_a = 0;
					}
			}				
	
	}
}

